package ipaddr

import (
	"io"
	"log/slog"
	"net"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"sync"
	"time"

	"github.com/gofiber/fiber/v2"
	"github.com/lionsoul2014/ip2region/binding/golang/xdb"
	"golang.org/x/text/encoding/simplifiedchinese"
	"golang.org/x/text/transform"
)

var (
	ipUtilTemp *ipUtil

	ipUtilOnce sync.Once
)

type ipUtil struct {
	address string
}

func New() *ipUtil {
	ipUtilOnce.Do(func() {
		ipUtilTemp = &ipUtil{
			address: "-",
		}
	})
	return ipUtilTemp
}

// GetIP 获取客户端真实 IP 地址
func (m *ipUtil) GetIP(c *fiber.Ctx) string {
	// 优先使用 X-Forwarded-For，如果为空则使用 X-Real-IP
	clientIP := c.Get("X-Real-IP")
	if clientIP == "" {
		clientIP = c.Get("X-Forwarded-For")
		ip := strings.Split(clientIP, ",")[0]
		re := regexp.MustCompile(`[^0-9.]`)
		clientIP = re.ReplaceAllString(ip, "")
	}
	// 如果都为空，使用远程地址
	if clientIP == "" {
		clientIP = c.IP()
	}
	return clientIP
}

// GetRealAddressByIP 解析IP地址
func (m *ipUtil) GetRealAddressByIP(ip string) string {
	// ip = "183.53.190.133"
	if ip == "" {
		return m.address
	}
	if ip == "127.0.0.1" {
		return "localhost"
	}
	parseIP := net.ParseIP(ip)
	if parseIP != nil && parseIP.IsPrivate() {
		return m.address
	}
	addr, err := m.GetIpAddr(ip)
	if err != nil {
		slog.Error("IP地址解析异常:" + err.Error())
	}
	return addr
}

func (m *ipUtil) GetIpAddr(ip string) (string, error) {
	addr, err := m.GetByRemote(ip)
	if err != nil {
		addr, err = m.GetByLocal(ip)
		if err != nil {
			return m.address, err
		}
	}
	return addr, err
}

func (m *ipUtil) GetByLocal(ip string) (string, error) {
	stRootDir, _ := os.Getwd()
	fPath := filepath.Join(stRootDir, "resources", "ip2region.xdb")
	searcher, err := xdb.NewWithFileOnly(fPath)
	if err != nil {
		slog.Error("解析IP地址xdb数据库异常:" + err.Error())
		return m.address, err
	}
	defer searcher.Close()

	// do the search
	region, err := searcher.SearchByStr(ip)
	if err != nil {
		slog.Error("解析IP地址查找异常:" + err.Error())
		return m.address, err
	}
	return region, nil
}

// GetByRemote 通过远程请求获取登录地址
func (m *ipUtil) GetByRemote(ip string) (string, error) {
	// 创建GET请求
	client := &http.Client{
		Timeout: 2 * time.Second, // 设置超时时间
	}
	resp, err := client.Get("http://whois.pconline.com.cn/ip.jsp?ip=" + ip)
	if err != nil {
		return m.address, err
	}
	defer resp.Body.Close()
	bodyReader := transform.NewReader(resp.Body, simplifiedchinese.GBK.NewDecoder())
	// 响应内容
	body, err := io.ReadAll(bodyReader)
	if err != nil {
		return m.address, err
	}
	return strings.ReplaceAll(string(body), "\n", ""), err
}
